package com.zmn.mcc.business.impl.staff;

import com.zmn.common.constant.StatusConsts;
import com.zmn.common.dictionary.GlobalDict;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.mcc.business.interfaces.staff.StaffLoginBService;
import com.zmn.mcc.common.dto.staff.LoginStaffDO;
import com.zmn.mcc.cache.DeptCache;
import com.zmn.mcc.cache.RoleCache;
import com.zmn.mcc.cache.StaffCache;
import com.zmn.mcc.manager.config.MccConfig;
import com.zmn.mcc.manager.utils.PasswordUtil;
import com.zmn.mcc.model.entity.dept.McDept;
import com.zmn.mcc.model.entity.role.McRole;
import com.zmn.mcc.model.entity.staff.McStaff;
import com.zmn.mcc.model.entity.staff.McStaffQuery;
import com.zmn.mcc.services.interfaces.role.RoleStaffService;
import com.zmn.mcc.services.interfaces.staff.StaffService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author heguanghua
 * @since 2020/06/11 16:43
 */
@Service
public class StaffLoginBServiceImpl implements StaffLoginBService {
    protected Logger logger = LoggerFactory.getLogger(StaffLoginBServiceImpl.class);

    // region Resource
    @Resource
    private StaffService staffService;
    @Resource
    private RoleStaffService roleStaffService;
    @Resource
    private PasswordUtil passwordUtil;
    @Resource
    private StaffCache staffCache;
    @Resource
    private DeptCache deptCache;
    @Resource
    private RoleCache roleCache;
    @Resource
    private MccConfig config;
    // endregion

    BeanCopier copier = BeanCopier.create(McStaff.class, LoginStaffDO.class, false);

    // region 校验员工登录

    @Override
    //@ReadOnlyConnection
    public ResponseDTO checkLogin(String username, String password, String loginIP) {

        if (StringUtil.isBlank(username)) {
            return ResponseDTO.fail(StatusConsts.FAIL_LOGIN_USERPWD_EMPTY, "登录名称不能为空");
        }
        if (StringUtil.isBlank(password)) {
            return ResponseDTO.fail(StatusConsts.FAIL_LOGIN_USERPWD_EMPTY, "登录密码不能为空");
        }
        if (StringUtil.isBlank(loginIP)) {
            return ResponseDTO.fail(StatusConsts.ERROR_PARAMS, "登录IP不能为空");
        }

        try {

            username = username.trim();
            password = password.trim();

            /**
             * 查询员工
             * 手机号码优先
             */
            McStaffQuery query = new McStaffQuery();
            if (StringUtil.isMobile(username)) {
                query.setMobile(username);
            } else {
                query.setStaffName(username);
            }
            McStaff staff = staffService.findForLogin(query);
            if (staff == null) {
                return ResponseDTO.fail(StatusConsts.ERROR_NOTUSER, "账号不存在");
            }

            // 员工ID
            int staffId = staff.getStaffId();

            /**
             * 校验状态
             */
            if (staff.getStatus() != GlobalDict.STATUS_ENABLE) {
                return ResponseDTO.fail(StatusConsts.FAIL_LOGIN_DISABLE, "账号已被禁用或锁定");
            }

            /**
             * 检验密码错误次数
             */
            Integer loginErrorCount = staffCache.getLoginErrorCount(staffId);
            if (NumberUtil.isNotNullOrZero(loginErrorCount) && loginErrorCount >= config.getLoginErrorLimit()) {
                return ResponseDTO.fail(StatusConsts.FAIL_LOGIN_TOO_MANY_TIMES, String.format("密码错误次数已超过%s次", config.getLoginErrorLimit()));
            }

            /**
             * 校验IP白名单
             */

            /**
             * 校验登录密码
             * 优先校验原始密码，不通过则校验临时密码
             */
            boolean checkPwd = passwordUtil.checkPassword(password, staff.getSalt(), staff.getPassword());
            if (!checkPwd) {
                String tmpPwd = staffCache.getTempPwd(staffId);
                if (StringUtil.isNotBlank(tmpPwd)) {
                    checkPwd = passwordUtil.checkPassword(password, staff.getSalt(), tmpPwd);
                }
            }
            if (!checkPwd) {
                // 密码错误次数+1
                staffCache.setLoginErrorCount(staffId);
                return ResponseDTO.fail(StatusConsts.FAIL_LOGIN_PASSWORD, "登录密码错误");
            }

            // 密码错误次数复位
            staffCache.deleteLoginErrorCount(staffId);

            /**
             * 对象转换
             */
            LoginStaffDO token = new LoginStaffDO();
            copier.copy(staff, token, null);

            /**
             * 员工所属部门
             * 所属部门的数据权限继承部门ID
             */
            McDept dept = deptCache.getDept(staff.getDeptId());
            if (dept != null) {
                token.setDpermitDeptId(dept.getDpermitDeptId());
            }

            /**
             * 读取员工角色
             * 如员工未自定义功能权限，则继承部门默认角色
             */
            List<Integer> roleIds = new ArrayList<>();
            if (staff.getPermitSelf() == GlobalDict.YESNO_YES) {
                roleIds = roleStaffService.listRoleIdByStaffId(staffId);
            } else {
                if (dept != null && NumberUtil.isNotNullOrZero(dept.getRoleId())) {
                    roleIds.add(dept.getRoleId());
                }
            }

            // 数据导出上限、查询前溯天数、可用角色ID
            int exportLimit = 0;
            int dayLimit = 0;
            int numberLimit = 0;
            List<Integer> validRoleIds = new ArrayList<>();
            for (int roleId : roleIds) {

                McRole role = roleCache.getRole(roleId);
                if (role.getStatus() != GlobalDict.STATUS_ENABLE) {
                    continue;
                }
                validRoleIds.add(roleId);

                int limit = Optional.ofNullable(role.getExportLimit()).orElse(config.getDataExportLimit());
                if (limit > exportLimit) {
                    exportLimit = limit;
                }
                int limit2 = Optional.ofNullable(role.getDayLimit()).orElse(config.getDataDayLimit());
                if (limit2 > dayLimit) {
                    dayLimit = limit2;
                }
                int limit3 = Optional.ofNullable(role.getNumberLimit()).orElse(config.getDataNumberLimit());
                if (limit3 > numberLimit) {
                    numberLimit = limit3;
                }
            }
            if (exportLimit > config.getDataExportLimit()) {
                exportLimit = config.getDataExportLimit();
            }
            if (dayLimit > config.getDataDayLimit()) {
                dayLimit = config.getDataDayLimit();
            }
            if (numberLimit > config.getDataNumberLimit()) {
                numberLimit = config.getDataNumberLimit();
            }
            token.setExportLimit(exportLimit);
            token.setDayLimit(dayLimit);
            token.setNumberLimit(numberLimit);
            token.setRoleIds(validRoleIds);

            /**
             * 校验不限IP日期
             */
            boolean isOpenDate = staffCache.getOpenDateExists(staffId);
            token.setOpenDate(isOpenDate ? GlobalDict.YESNO_YES : GlobalDict.YESNO_NO);

            /**
             * 更新最后一次登录时间
             */
            staffService.updateLastLoginTime(staffId);

            logger.info(String.format("[ZMN]员工登录校验成功,username:[%s],loginIP:[%s]", username, password));
            return ResponseDTO.success(token);
        } catch (Exception ex) {
            logger.error(String.format("[ZMN]员工登录校验失败,username:[%s],loginIP:[%s],ex:[%s]", username, password, ex.getMessage()));
            return ResponseDTO.fail("员工登录校验失败");
        }
    }
    // endregion

}
